#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"

typedef void (* subwork_t)();

class Channel
{
public:
    Channel(int wfd, pid_t pid, const std::string &name)
        : _wfd(wfd), _subProcessPid(pid), _name(name)
    {
    }
    int GetWfd() const
    {
        return _wfd;
    }
    int GetPid()
    {
        return _subProcessPid;
    }
    const std::string &GetName()
    {
        return _name;
    }
    void CloseChannel()
    {
        close(_wfd);
    }
    void Wait() const
    {
        int rid = waitpid(_subProcessPid, nullptr, 0);
        if (rid > 0)
        {
            std::cout << "wait " << rid << " process succeed" << std::endl;
        }
        else
            std::cerr << "waitpid fail" << std::endl;
    }

private:
    int _wfd;             // 管道写端
    pid_t _subProcessPid; // 管道对应子进程pid
    std::string _name;
};

// // 子进程工作
// void Work(int rfd)
// {
//     while (true)
//     {
//         int taskIndex;
//         int n = read(rfd, &taskIndex, sizeof(taskIndex)); // 读出任务下标
//         pid_t pid = getpid();
//         if (n == sizeof(int))
//         {
//             std::cout << "pid is " << pid << " handler task" << std::endl;
//             ExecuteTask(taskIndex, pid); // 执行对应任务函数;
//             sleep(1);
//         }
//         else if (n == 0)//管道写端关闭, 直接读到了结尾, 返回0; 
//         {
//             std::cout << pid << " wfd quit, read return 0" << std::endl;
//             break;
//         }
//         else //n < 0
//         {
//             std::cerr << "read fail" << std::endl;
//             break;
//         }
//     }
// }

// <形参类型和命名规范>
// const &: 输入型
// & : 输入输出型参数
// * : 输出型参数
//void CreateChannelAndSub(int num, std::vector<Channel> *channels)
void CreateChannelAndSub(int num, std::vector<Channel> *channels, subwork_t subWork)//细节优化2, 回调函数
{
    for (size_t i = 0; i < num; i++)
    {
        // 1.创建管道
        int pipefd[2] = {0};
        pipe(pipefd);

        // 2.创建子进程
        pid_t pid = fork();
        if (pid == 0)
        {
            // child
            //根本解决bug
            if (!channels->empty())
            {
                //关闭(不为空)第二个之后, 子进程fd表中的所有管道写端
                for (auto& channel : *channels) channel.CloseChannel();
            }
            close(pipefd[1]);
            //Work(pipefd[0]); // 读任务, 工作
            //细节优化1
            dup2(pipefd[0], 0);//将管道的读端，重定向到标准输入; 后面直接从标准输入读取
            subWork();
            close(pipefd[0]);

            exit(0);
        }
        // father
        close(pipefd[0]);                                   // 关闭每个管道中父进程中的读端
        std::string name = "channel_" + std::to_string(i);  // 构建个名字
        channels->push_back(Channel(pipefd[1], pid, name)); // 匿名构造; 管道对象
        // channels->push_back({pipefd[1], pid, name}); // 多参数构造函数的隐式类型转换
    }
}
// 选一个管道
int SelectChannel(int sz)
{
    // 轮询选一个子进程下标
    static int next = 0;
    int channel = next % sz;
    next++;
    return channel;
}
// 向管道发送任务命令(任务下标)
void SendTaskCommand(const Channel &channel, int taskCommand)
{
    write(channel.GetWfd(), &taskCommand, sizeof(taskCommand));
}
// 控制一次进程
void CtrlSubProcessOnce(const std::vector<Channel> &channels)
{
    // 1.选一个子进程/信道
    int channelIndex = SelectChannel(channels.size());
    // 2.选一个任务
    int taskCommand = SelectTask();
    // 3.发送任务
    SendTaskCommand(channels[channelIndex], taskCommand);
}
// 控制ctrlTimes次进程
void CtrlSubProcess(const std::vector<Channel> &channels, int ctrlTimes = -1)
{
    if (ctrlTimes > 0)
    {
        while (ctrlTimes--)
        {
            CtrlSubProcessOnce(channels);
        }
    }
    else // 无限次数
    {
        while (true)
        {
            CtrlSubProcessOnce(channels);
        }
    }
}
void CleanupChannel(const std::vector<Channel> channels)
{
    // 关闭管道
    for (auto &channel : channels)
    {
        close(channel.GetWfd()); // 关闭写端;
        // 一个管道的写端关闭后, 通过代码让读端也关闭, 跳出子进程反复读的循环, 结束子进程, 父进程等待;
        channel.Wait();//等待僵尸子进程  //bug-----管道为空 并且 写端没有真正关闭时, read读端会一直阻塞不退出
    }


    //1.方法一: 避免

    // // 关闭管道
    // for (auto &channel : channels)
    // {
    //     close(channel.GetWfd()); // 关闭写端;
    //     // 一个管道的写端关闭后, 通过代码让读端也关闭, 跳出子进程反复读的循环, 结束子进程, 父进程等待;
    //     channel.Wait();//bug
    // }
    // // 等待僵尸子进程
    // for (int i = 0; i < channels.size(); i++)
    // {
    //     channels[i].Wait();
    // }

    // 或或或

    // int n = channels.size() - 1;
    // while (n)
    // {
    //     close(channels[n].GetWfd()); // 关闭写端;
    //     channels[n--].Wait();
    // }
}
int main(int argc, char *argv[]) // argc是参数个数(包含命令本身)
{
    // 检查一下
    if (argc != 2) // argv = 命令 + 子进程个数
    {
        std::cerr << "usage: " << argv[0] << " subProcessCount: " << argv[1] << std::endl;
        return -1;
    }
    // 1.获取子进程个数
    int subProcessCount = std::stoi(argv[1]); // argv[0]是命令, argv[1]是第一个选项
    // 2.加载任务
    LoadTask();
    // 3.创建进程池对象
    std::vector<Channel> channels; // 进程池对象
    // 4.创建信道和子进程
    CreateChannelAndSub(subProcessCount, &channels, Work1);
    // 5.通过Channel控制子进程
    CtrlSubProcess(channels, 5);
    // 6.关闭管道+子进程
    CleanupChannel(channels);

    return 0;
}